#include "timer.h"

Timer::Timer(uint64_t period, bool circulation)
:m_fun(nullptr)
,m_period(period)
,m_runTime(0)
,m_circulation(circulation)
{

}


uint64_t Timer::GetNowTime()
{
    auto sc = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::steady_clock::now());
    auto time = std::chrono::duration_cast<std::chrono::milliseconds>(sc.time_since_epoch());

    return time.count();
}

void Timer::SetRunTime()
{   
    this->m_runTime = GetNowTime() + this->m_period;
}


TimerManager::TimerManager(uint32_t threadNum):m_stop(true),m_threadPool(threadNum)
{
    m_thread = std::thread(&TimerManager::_Run, this);
}

TimerManager::~TimerManager()
{
    Stop();
    m_thread.join();
}


void TimerManager::_Run()
{
    std::chrono::milliseconds timeout(UINT64_MAX);
    while(m_stop)
    {
        std::unique_lock<std::mutex> lock(m_taskMutex);
        if (m_condition.wait_for(lock, timeout) == std::cv_status::timeout)  //条件变量超时
        {
            if(m_tasks.empty())
            {
                timeout = std::chrono::milliseconds(UINT64_MAX);
                continue;
            }

            uint64_t nowTime = Timer::GetNowTime();
            for(auto it = this->m_tasks.begin(); it != this->m_tasks.end();)
            {
                if(nowTime >= it->second->GetRunTime())
                {
                    m_threadPool.EnQueue(it->second->GetFunc());
                    Timer::ptr tempTimer = it->second;
                    it = m_tasks.erase(it);
                    if(tempTimer->GetCirculation())
                    {
                        tempTimer->SetRunTime();
                        m_tasks.insert(std::make_pair(tempTimer->GetRunTime(), tempTimer));
                    }
                }
                else
                {
                    timeout = std::chrono::milliseconds(m_tasks.begin()->second->GetRunTime() - nowTime);  
                    break;
                }
            }
        }
        else //条件变量被唤醒
        {
            timeout = std::chrono::milliseconds(m_tasks.begin()->second->GetPeriod());   
        }
    }
}
